package com.sf.fy.uav.applet.operate.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.alibaba.fastjson.JSONArray;
import com.sf.fy.uav.applet.operate.service.ActivityService;
import com.sf.fy.uav.applet.order.service.OrderService;
import com.sf.fy.uav.common.enums.*;
import com.sf.fy.uav.common.request.operate.WithdrawalRequest;
import com.sf.fy.uav.common.response.operational.CheckSigningResponse;
import com.sf.fy.uav.common.response.operational.PromotionRecordsResponse;
import com.sf.fy.uav.common.response.operational.RewardBillResponse;
import com.sf.fy.uav.common.response.operational.RewardDetailsResponse;
import com.sf.fy.uav.common.utils.BeanUtil;
import com.sf.fy.uav.common.utils.PhoneEncoderUtil;
import com.sf.fy.uav.common.utils.RedisPrefixKey;
import com.sf.fy.uav.entity.PromotionRecords;
import com.sf.fy.uav.mapper.PromotionRecordsMapper;
import com.sf.fy.uav.mapper.SigningCallbackInfoMapper;
import com.sf.fy.uav.sms.rest.WxAppletRest;
import com.sf.fy.uav.user.controller.UserWalletRest;
import com.sf.fy.uav.user.entity.RewardBill;
import com.sf.fy.uav.user.entity.User;
import com.sf.fy.uav.user.entity.UserWallet;
import com.sf.fy.uav.user.mapper.RewardBillMapper;
import com.sf.fy.uav.user.mapper.UserMapper;
import com.sf.fy.uav.user.mapper.UserWalletMapper;
import com.sf.fy.uav.user.rest.RewardBillRest;
import com.sf.fy.uav.user.rest.UserRest;
import com.sf.fy.uav.withdrawal.entity.Dictionary;
import com.sf.fy.uav.withdrawal.rest.DictionaryRest;
import com.sfpay.sdk.util.UUID;
import org.apache.commons.codec.digest.DigestUtils;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.stream.Collectors;

/**
 * @author 01430312
 */
@Service
public class ActivityInfoServiceImpl implements ActivityService {

    @Value("${lock.try_time:3}")
    private long lockTryTime;

    @Autowired
    private SigningCallbackInfoMapper signingCallbackInfoMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private UserWalletMapper userWalletMapper;

    @Autowired
    private UserWalletRest userWalletRest;

    @Value("${cloud.project_id: 301}")
    private String cloudProjectId;

    @Autowired
    private PromotionRecordsMapper promotionRecordsMapper;

    @Autowired
    private RewardBillMapper rewardBillMapper;

    @Value("${cloud.app_secret:5a6d5675655739755a773d3d}")
    private String cloudAppSecret;


    @Value("${cloud.key:35596947354c326a}")
    private String cloudKey;

    @Autowired
    private PhoneEncoderUtil phoneEncoderUtil;


    @Value("${callback.url:https://ie-uocs-out.sit.sf-express.com:4433/uocs-intracity/}")
    private String callbackUrl;

    @Value("${cloud.sub_mid:1605110668}")
    private String cloudSubMid;

    @Value("${cloud.sub_app_id:wxb52f88af86dd54e9}")
    private String cloudSubAppId;

//    @Value("${cloud.open_id:oBzgot2d5vM4nxiPxxLQVK6UTXpo}")
//    private String cloudOpenId;

    @Value("${cloud.minimum_amount:0.3}")
    private BigDecimal cloudMinimumAmount;

    @Autowired
    private WxAppletRest wxAppletRest;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private OrderService orderService;

    @Autowired
    private UserRest userRest;

    @Autowired
    private DictionaryRest dictionaryRest;

    @Autowired
    private RewardBillRest rewardBillRest;

    @Autowired
    private RedissonClient redissonClient;


    @Override
    public RewardDetailsResponse rewardDetails(Integer page, Integer size, String userId) {
        RewardDetailsResponse rewardDetailsResponse = new RewardDetailsResponse();
        UserWallet userWallet = userWalletRest.selectOneByUserId(userId);
        if (!ObjectUtils.isEmpty(userWallet)) {
            rewardDetailsResponse.setAccumulatedRewards(userWallet.getAccumulatedRewards());
            rewardDetailsResponse.setAccumulatedWithdrawal(userWallet.getAccumulatedWithdrawal());
            rewardDetailsResponse.setWithdrawableBalance(userWallet.getBalance());
        }
        QueryWrapper<PromotionRecords> promotionRecordsQueryWrapper = new QueryWrapper<>();
        promotionRecordsQueryWrapper.eq("promotion_user_id", userId);
        promotionRecordsQueryWrapper.isNotNull("finish_time");
        promotionRecordsQueryWrapper.orderByDesc("finish_time");
        Page<PromotionRecords> promotionRecordsPage = new Page<>(page, size);
        IPage<PromotionRecords> promotionRecordsIPage = promotionRecordsMapper.selectPage(promotionRecordsPage, promotionRecordsQueryWrapper);
        List<PromotionRecords> promotionRecordsList = promotionRecordsIPage.getRecords();
        List<PromotionRecordsResponse> promotionRecordsResponseList = BeanUtil.copyProperties(promotionRecordsList, PromotionRecordsResponse.class);
        promotionRecordsResponseList.forEach(promotionRecordsResponse -> {
            promotionRecordsResponse.setType(RewardTypeEnum.TRANSACTION_REWARD.getType());
            Byte rewardType = promotionRecordsResponse.getRewardType();
            promotionRecordsResponse.setRewardTypeName(ActivityProductTypeEnum.getName(rewardType));
            Date finishTime = promotionRecordsResponse.getFinishTime();
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(finishTime);
            calendar.set(Calendar.HOUR_OF_DAY, 0);
            calendar.set(Calendar.MINUTE, 0);
            calendar.set(Calendar.SECOND, 0);
            calendar.set(Calendar.MILLISECOND, 0);
            promotionRecordsResponse.setFinishDate(calendar.getTime());
            //推广奖励金额字段，数据库精度为小数点后4位，处理成2位
            promotionRecordsResponse.setPromotionReward(promotionRecordsResponse.getPromotionReward().setScale(2, RoundingMode.HALF_UP));
        });


        Map<Date, List<PromotionRecordsResponse>> stationMap = promotionRecordsResponseList.stream().collect(Collectors.groupingBy(PromotionRecordsResponse::getFinishDate, TreeMap::new, Collectors.toList())).descendingMap();
        rewardDetailsResponse.setDateListMap(stationMap);
        return rewardDetailsResponse;
    }

    @Override
    public Long getBillsCount(String userId) {
        QueryWrapper<RewardBill> rewardBillQueryWrapper = new QueryWrapper<>();
        rewardBillQueryWrapper.lambda().eq(RewardBill::getUserId, userId).eq(RewardBill::getType, BillTypeEnum.WITHDRAWAL.getValue());
        return rewardBillMapper.selectCount(rewardBillQueryWrapper);
    }

    @Override
    public Long rewardDetailsTotal(String userId) {
        QueryWrapper<PromotionRecords> promotionRecordsQueryWrapper = new QueryWrapper<>();
        promotionRecordsQueryWrapper.eq("promotion_user_id", userId);
        promotionRecordsQueryWrapper.isNotNull("finish_time");
        return promotionRecordsMapper.selectCount(promotionRecordsQueryWrapper);
    }

    @Override
    public List<RewardBillResponse> getBills(Integer page, Integer size, String userId) {
        QueryWrapper<RewardBill> rewardBillQueryWrapper = new QueryWrapper<>();
        rewardBillQueryWrapper.lambda().eq(RewardBill::getType, BillTypeEnum.WITHDRAWAL.getValue()).eq(RewardBill::getUserId, userId).orderByDesc(RewardBill::getCreateTime);
        Page<RewardBill> rewardBillPage = new Page<>(page, size);
        IPage<RewardBill> rewardBillIPage = rewardBillMapper.selectPage(rewardBillPage, rewardBillQueryWrapper);
        return BeanUtil.copyProperties(rewardBillIPage.getRecords(), RewardBillResponse.class);
    }

    @Override
    public CheckSigningResponse checkSigning(String userId) {
        CheckSigningResponse checkSigningResponse = new CheckSigningResponse();
        Integer auditStatus = null;
        User user = userMapper.selectById(userId);
        if (!ObjectUtils.isEmpty(user)) {
            String nonce = UUID.randomUUID().replaceAll("-", "");
            Long timeStamp = System.currentTimeMillis();
            String sign = this.getSign(cloudAppSecret, nonce, timeStamp);
            String phoneNumber = phoneEncoderUtil.decryptString(user.getPhoneNumber());
            Object obj = wxAppletRest.checkSign(phoneNumber, sign, timeStamp, nonce);
            Integer code = (Integer) ((JSONObject) obj).get("code");
            if (!code.equals(200)) {
                throw new RuntimeException(((JSONObject) obj).get("message").toString());
            }
            auditStatus = (Integer) ((Map) ((JSONObject) obj).get("data")).get("auditStatus");
        }
        checkSigningResponse.setAuditStatus(auditStatus);
        if (!ObjectUtils.isEmpty(auditStatus)) {
            if (auditStatus.equals(1)) {
                String signingUrl = this.getSigningUrl(userId);
                checkSigningResponse.setSigningUrl(signingUrl);
            }
        }

        return checkSigningResponse;
    }

    @Override
    public String getSigningUrl(String userId) {
        String url = null;
        User user = userMapper.selectById(userId);
        if (!ObjectUtils.isEmpty(user)) {
            String nonce = UUID.randomUUID().replaceAll("-", "");
            Long timeStamp = System.currentTimeMillis();
            String sign = this.getSign(cloudAppSecret, nonce, timeStamp);
            String phoneNumber = phoneEncoderUtil.decryptString(user.getPhoneNumber());
            Object obj = wxAppletRest.getSign(phoneNumber, sign, timeStamp, nonce, userId);
//        String auditReason = (String) ((Map) ((JSONObject) obj).get("data")).get("auditReason");
            url = (String) ((JSONObject) obj).get("data");
        }
//        Sign signObj = new Sign();
        return url;
    }

    @Override
    public Object getSignInfo(String phoneNumber) {
        String nonce = UUID.randomUUID().replaceAll("-", "");
        Long timeStamp = System.currentTimeMillis();
        String sign = this.getSign(cloudAppSecret, nonce, timeStamp);
        return wxAppletRest.getSignInfo(phoneNumber, sign, timeStamp, nonce);
    }

    @Override
    public BigDecimal withdrawal(WithdrawalRequest withdrawalRequest) {
        BigDecimal withdrawalAmount = withdrawalRequest.getWithdrawalAmount();
        if (withdrawalAmount.stripTrailingZeros().scale() > 2) {
            throw new RuntimeException("提现金额最多只能两位小数");
        }
        Lock lock = null;
        try {
            String userId = withdrawalRequest.getUserId();
            lock = redissonClient.getLock(ServicePrefixEnum.SAME_CITY + RedisPrefixKey.APPLET_WITHDRAWAL + userId);
            boolean b = lock.tryLock(0, TimeUnit.SECONDS);
            if (b) {


                //业务
                UserWallet userWallet = null;
                if (withdrawalAmount.compareTo(cloudMinimumAmount) < 0) {
                    throw new RuntimeException("提现金额不能小于" + cloudMinimumAmount + "元");
                }
                //正常用户才能提现
                User user = userRest.getNormalUser(userId);
                String openId = (String) redisTemplate.opsForValue().get(ServicePrefixEnum.SAME_CITY + RedisPrefixKey.APPLET_OPENID + userId);
                if (ObjectUtils.isEmpty(openId)) {
                    String loginCode = withdrawalRequest.getLoginCode();
                    if (!ObjectUtils.isEmpty(loginCode)) {
                        openId = orderService.getNewOpenId(loginCode);
                    }
                }
                if (!ObjectUtils.isEmpty(user)) {
                    userWallet = userWalletRest.selectOneByUserId(userId);
                    if (!ObjectUtils.isEmpty(userWallet)) {
                        BigDecimal balance = userWallet.getBalance();
                        if (balance.compareTo(withdrawalAmount) < 0) {
                            throw new RuntimeException("余额不足");
                        }
                        //判断是否超过限制
                        Dictionary dictionary = dictionaryRest.getBySubject(DictionarySubjectEnum.WITHDRAWAL_PERSONAL_DAILY_AMOUNT.getCode());
                        if (!ObjectUtils.isEmpty(dictionary)) {
                            BigDecimal bigDecimal = dictionary.getDecimalValue();
                            BigDecimal dailyWithdrawal = rewardBillRest.getDailyWithdrawalByUserId(userId);
                            if (withdrawalAmount.add(dailyWithdrawal).compareTo(bigDecimal) > 0) {
                                throw new RuntimeException("已超出当日提现限额（" + bigDecimal.setScale(2, RoundingMode.HALF_UP) + "元）");
                            }
                        }
                        Dictionary totalSettingDictionary = dictionaryRest.getBySubject(DictionarySubjectEnum.WITHDRAWAL_TOTAL_DAILY_AMOUNT.getCode());
                        if (!ObjectUtils.isEmpty(totalSettingDictionary)) {
                            BigDecimal bigDecimal = totalSettingDictionary.getDecimalValue();
                            BigDecimal totalDailyWithdrawal = rewardBillRest.getTotalDailyWithdrawal();
                            if (withdrawalAmount.add(totalDailyWithdrawal).compareTo(bigDecimal) > 0) {
                                throw new RuntimeException("已超出当日提现最高额度");
                            }
                        }
                        String phoneNumber = user.getPhoneNumber();

                        Object obj = this.getSignInfo(phoneEncoderUtil.decryptString(phoneNumber));
                        String bankName = (String) ((JSONObject) ((JSONObject) obj).get("data")).get("bankName");
                        String bankNum = (String) ((JSONObject) ((JSONObject) obj).get("data")).get("bankAccount");
                        String qkName = (String) ((JSONObject) ((JSONObject) obj).get("data")).get("qkName");
                        String qkId = (String) ((JSONObject) ((JSONObject) obj).get("data")).get("qkId");
                        if (!((JSONObject) obj).get("code").equals(200)) {
                            throw new RuntimeException(((JSONObject) obj).get("message").toString());
                        }


                        String qkPhone = phoneEncoderUtil.decryptString(phoneNumber);
                        String nonce = UUID.randomUUID().replaceAll("-", "");
                        Long timeStamp = System.currentTimeMillis();
                        String sign = this.getSign(cloudAppSecret, nonce, timeStamp);
                        String tcWaybillNo = UUID.randomUUID().replaceAll("-", "");
                        String externalNumber = UUID.randomUUID().replaceAll("-", "");
                        Date date = new Date();
                        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                        /**
                         * 要先查询是否已经发起提现了，避免重复提现啊，不做回调也好，其实！如果回调才扣除余额，回调出问题那么就容易导致重复扣除余额，通常都不会扣除失败，如果扣除失败就是账户余额不足啥的，人工解决即可！！！
                         */
                        String params = " {\n" + "    \"body\":{\n" + "        \"batchName\":\"" + "提现转账" + "\",\n" + "        \"batchRemark\":\"" + "提现转账" + "\",\n" + "        \"endTime\":\"" + sdf.format(date) + " 23:59:59" + "\",\n" + "        \"externalNumber\":\"" + externalNumber + "\",\n" + "        \"payUserDTOList\":[\n" + "            {\n" + "                \"bankAccount\":\"" + bankNum + "\",\n" + "                \"bankName\":\"" + bankName + "\",\n" + "                \"externalOrderNumber\":\"" + tcWaybillNo + "\",\n" + "                \"idcard\":\"" + qkId + "\",\n" + "                \"openId\":\"" + openId + "\",\n" + "                \"phone\":\"" + qkPhone + "\",\n" + "                \"transferAmount\":" + withdrawalAmount.multiply(new BigDecimal(100)) + ",\n" + "                \"transferRemark\":\"提现转账\",\n" + "                \"userName\":\"" + qkName + "\"\n" + "            }\n" + "        ],\n" + "        \"projectId\":" + cloudProjectId + ",\n" + "        \"asynUrl\": \"" + callbackUrl + "home/fund/management/refund_record\",\n" + "        \"startTime\":\"" + sdf.format(date) + " 00:00:00" + "\",\n" + "        \"subAppId\":\"" + cloudSubAppId + "\",\n" + "        \"subMid\":\"" + cloudSubMid + "\",\n" + "        \"totalAmount\":" + withdrawalAmount.multiply(new BigDecimal(100)) + ",\n" + "        \"totalNum\":1\n" + "    },\n" + "    \"key\":\"" + cloudKey + "\",\n" + "    \"nonce\":\"" + nonce + "\",\n" + "    \"sign\":\"" + sign + "\",\n" + "    \"timeStamp\":" + timeStamp + "\n" + "}\n";


                        Object object = wxAppletRest.wxPay(params);
                        if (((JSONObject) object).get("code").equals(200)) {
                            if (((Map) ((JSONArray) ((JSONObject) object).get("data")).get(0)).get("detailStatus").equals("SUCCESS")) {
                                userWallet.setUpdateTime(date);
                                userWallet.setBalance(userWallet.getBalance().subtract(withdrawalAmount));
                                if (ObjectUtils.isEmpty(userWallet.getAccumulatedWithdrawal())) {
                                    userWallet.setAccumulatedWithdrawal(withdrawalAmount);
                                } else {
                                    userWallet.setAccumulatedWithdrawal(userWallet.getAccumulatedWithdrawal().add(withdrawalAmount));
                                }
                                userWalletMapper.updateById(userWallet);
                                RewardBill rewardBill = new RewardBill();
                                rewardBill.setExternalNumber(externalNumber);
                                rewardBill.setTcWaybillNo(tcWaybillNo);
                                rewardBill.setCreateTime(date);
                                rewardBill.setUpdateTime(date);
                                rewardBill.setType(BillTypeEnum.WITHDRAWAL.getValue());
                                rewardBill.setPricePaid(withdrawalRequest.getWithdrawalAmount());
                                rewardBill.setUserId(userId);
                                rewardBill.setBalance(userWallet.getBalance());
                                rewardBillMapper.insert(rewardBill);
                            } else {
                                if ("OPENID_INVALID".equals(((JSONObject) ((JSONArray) ((JSONObject) object).get("data")).get(0)).get("failReason"))) {
                                    redisTemplate.delete(ServicePrefixEnum.SAME_CITY + RedisPrefixKey.APPLET_OPENID + userId);
                                    throw new RuntimeException("提现异常，请重试");
                                }
                                throw new RuntimeException(((Map) ((JSONArray) ((JSONObject) object).get("data")).get(0)).get("failReason").toString());
                            }

                        } else {
                            throw new RuntimeException(((JSONObject) object).get("msg").toString());
                        }
                    }
                }
                BigDecimal balance = new BigDecimal(0);
                if (!ObjectUtils.isEmpty(userWallet)) {
                    balance = userWallet.getBalance();
                }
                return balance;
            }
        } catch (Exception e) {
            try {
                throw e;
            } catch (InterruptedException ex) {
                throw new RuntimeException(ex);
            }
        } finally {
            if (!org.springframework.util.ObjectUtils.isEmpty(lock)) {
                // 释放锁
                lock.unlock();
            }
        }
        return null;

    }

    @Override
    public String getBalance(String userId) {
//    public BigDecimal getBalance(String userId) {
        User user = userMapper.selectById(userId);
        if (!ObjectUtils.isEmpty(user)) {
            UserWallet userWallet = userWalletRest.selectOneByUserId(userId);
            if (!ObjectUtils.isEmpty(userWallet)) {
                return userWallet.getBalance().toString();
            } else {
                Date date = new Date();
                userWallet = new UserWallet();
                userWallet.setCreateTime(date);
                userWallet.setUpdateTime(date);
                userWallet.setUserId(userId);
                userWallet.setBalance(new BigDecimal(0));
                userWallet.setAccumulatedRewards(new BigDecimal(0));
                userWalletMapper.insert(userWallet);
            }
        }
//        return new BigDecimal(0);
        return "0.00";
    }

    private String getSign(String appSecret, String nonce, Long timeStamp) {
        Map<String, Object> map = new HashMap<>(2);
        //流水号
        map.put("nonce", nonce);
//        map.put("nonce", "5245457890");
        //时间戳(毫秒)
//        map.put("timeStamp", 1693207002000L);
        map.put("timeStamp", timeStamp);
        //流水号+时间戳
        String param = map.get("nonce").toString() + map.get("timeStamp").toString();
        //商户id
//        String appsecret = "5a6d5675655739755a773d3d";
        //生成签名
        // 加密后的字符串
        return DigestUtils.md5Hex(param + appSecret);
    }

}
